import { Selection, BaseType } from 'd3-selection';
import L, { LatLng, Point } from 'leaflet';

import Feature from '../feature/feature';
import Line from '../feature/line';
import Polygon from '../feature/polygon';
import { FeatureJson, FeatureType, LayerOption } from '../types';
import { segmentWithLineCross } from '../geo/math';
import Vertex from '../feature/vertex';
import { getAnotation } from '../util/util';
import NodesLayer from './nodesLayer';

abstract class Layer {
    featureType: FeatureType;
    layerOption: LayerOption;
    layer: Selection<BaseType, unknown, null, undefined>;
    /**
     * 该图层包含的所有要素
     */
    features: Map<string, Feature> = new Map();

    constructor(layerOption: LayerOption, featureType: FeatureType) {
        this.featureType = featureType;
        this.layerOption = Object.assign({}, layerOption);
        this.layer = this.layerOption.surface.select(`g.layers .layer-${featureType}s`);
    }

    add(feature: Feature) {
        const existFeature = this.features.get(feature.id);

        // 不能重复添加id相同的要素
        if (existFeature) {
            return;
        }

        const { type, id } = feature;
        this.features.set(id, feature);
        const groupSelection = this.layer
            .selectAll(`g.${type}-group`)
            .data(this.features.values(), function (d) {
                return (d as Feature).id;
            })
            .join('g')
            .classed(`${type}-group`, true)
            .attr('id', (d) => {
                return d.id;
            })
            .filter((d) => d.id === id);

        feature.add(groupSelection);
    }

    redraw() {
        if (this.featureType === 'node') {
            (this as unknown as NodesLayer).resetData();
        }
        this.features.forEach((feature) => {
            feature.redraw();
        });
    }

    remove(id: string): Feature | undefined {
        const feature = this.features.get(id);
        if (feature) {
            feature.remove();
            //经纬度为空，表示feature已经remove，删除数组中的feature
            if (feature.latLngs.length === 0) {
                this.features.delete(id);
            }
        }
        return feature;
    }

    featureMousemove(e: MouseEvent, feature: Feature) {
        const modeType = this.layerOption.mode.getType();
        if (modeType !== 'draw' && modeType !== 'drag') {
            return;
        }
        // 然后计算交叉点,得到线上点的经纬度
        const cross = this.getCrossPoint(feature, { x: e.offsetX, y: e.offsetY });

        if (!cross) {
            return;
        }

        const drawFeature = <Line | Polygon>this.layerOption.mode.drawFeature;
        const selectFeature = <Vertex>this.layerOption.mode.selectedFeature;
        if (modeType === 'draw' && drawFeature) {
            // 自动吸附到线上
            drawFeature.drawMove(cross.latLng);
        } else if (modeType === 'drag' && selectFeature) {
            selectFeature.changeLatlng(cross.latLng, [0]);
            if (feature.type === 'line' || feature.type === 'polygon') {
                (feature as Line | Polygon).changeMidVertexVisible(true);
            }
        }
    }

    featureMouseout(e: MouseEvent, feature: Feature) {
        const modeType = this.layerOption.mode.getType();
        if (modeType === 'drag') {
            if (feature.type === 'line' || feature.type === 'polygon') {
                (feature as Line | Polygon).changeMidVertexVisible(false);
            }
        }
    }

    /**
     * 线和面的mouseup
     * @param e
     * @param feature 线或者面
     * @returns
     */
    featureMouseup(e: MouseEvent, feature: Feature) {
        const modeType = this.layerOption.mode.getType();
        const selectFeature = <Vertex>this.layerOption.mode.selectedFeature;
        debugger;
        if (modeType === 'drag' && selectFeature) {
            // 然后计算拖动点和线面交叉点,得到线上点的经纬度
            // 之所以用拖动点，不是e.offset,是因为拖动的点和线面有可能属于自相交,计算的交叉点位置不对
            const position = this.layerOption.transform.latLngToContainerPoint(selectFeature.latLngs[0][0]);
            const cross = this.getCrossPoint(feature, { x: position[0], y: position[1] });

            if (!cross) {
                return;
            }

            this.layerOption.changeMode('select', selectFeature);
            selectFeature.changeAttribute({ className: 'vertex cross' });
            selectFeature.addRelateFeatures([feature]);
            // 被点击要素添加个经纬度
            feature.latLngs[cross.sequence].splice(cross.index, 0, selectFeature.latLngs[0][0]);
            (feature as Line | Polygon).relateNodeIds[cross.sequence].splice(cross.index, 0, selectFeature.id);
            feature.redraw();
        }
    }

    /**
     * 线和面的点击事件
     * @param e
     * @param feature 线或者面
     * @returns
     */
    featureClick(e: MouseEvent, feature: Feature) {
        const drawFeature = <Line | Polygon>this.layerOption.mode.drawFeature;
        if (!drawFeature) {
            return;
        }

        const cross = this.getCrossPoint(feature, { x: e.offsetX, y: e.offsetY });

        if (!cross) {
            return;
        }

        const { latLng, index, sequence } = cross;

        // 添加个交叉点
        const show = true;
        const judgeRepeat = true;
        const vertexId = this.layerOption.nodesLayer!.addVertex(
            latLng,
            [drawFeature, feature],
            {
                className: 'vertex cross',
            },
            judgeRepeat,
            show
        );
        if (!vertexId) {
            return;
        }
        // 添加基类
        this.layerOption.addHistory(
            {
                annotation: getAnotation('add base'),
                features: [drawFeature, feature],
            },
            true
        );
        // 被点击要素添加个经纬度
        feature.latLngs[sequence].splice(index, 0, latLng);
        (feature as Line | Polygon).relateNodeIds[sequence].splice(index, 0, vertexId);
        feature.redraw();

        // 不用添加顶点了，已经addVertex过
        drawFeature.relateNodeIds[0].push(vertexId); // 这行要放在addLatLng前面，要不然addHistory拿不到正确的relateNodeIds
        drawFeature.addLatLng(latLng, false);

        this.layerOption.addHistory({
            annotation: getAnotation('add cross ', drawFeature),
            features: [drawFeature, feature],
        });
    }

    getCrossPoint(feature: Feature, position: { x: number; y: number }) {
        const { transform } = this.layerOption;
        const ePoint = new L.Point(position.x, position.y);
        // 被点击线,(e.offsetX,需要使用latLngToContainerPoint转换)
        const featurePoints = (feature as Line | Polygon).latLngs.map((latLngItem) => {
            return latLngItem.map((latLng) => {
                const point = transform.latLngToContainerPoint(latLng);
                return new L.Point(point[0], point[1]);
            });
        });

        // 交叉点和feature需要插入的点的index
        const { point, index, sequence } = segmentWithLineCross(ePoint, featurePoints);
        if (!point || !point.x) {
            return;
        }
        let crossLatlngArr = transform.containerPointToLatLng(point);
        const latLng = new LatLng(crossLatlngArr[0], crossLatlngArr[1]);
        return { latLng, point, index, sequence };
    }

    // 监听事件
    on() {}

    off() {}

    draw(latLng: LatLng) {}

    drawMove(latLng: LatLng) {}

    /**
     * 从数据库读取数据渲染 / 撤销重做操作渲染数据
     * @param features
     */
    abstract render(features: FeatureJson[]): Feature[];
}

export default Layer;
